#include "drvLcd.h"
#include "stm32f7xx_hal.h"
#include "rk043fn48h.h"
#include <string.h>

#define LCD_DISP_GPIO_PORT GPIOI
#define LCD_DISP_PIN GPIO_PIN_12
#define LCD_BL_CTRL_GPIO_PORT GPIOK
#define LCD_BL_CTRL_PIN GPIO_PIN_3

LTDC_HandleTypeDef LtdcHandle;
DMA2D_HandleTypeDef Dma2dHandle;
uint8_t CurrentUsedBufId = 0;

uint32_t LcdBufPing[LCD_LAYER_ROW * LCD_LAYER_COLUMN * BYTE_PER_PIXEL / 4] __attribute__((section(".sdram")));
uint32_t LcdBufPong[LCD_LAYER_ROW * LCD_LAYER_COLUMN * BYTE_PER_PIXEL / 4] __attribute__((section(".sdram")));

void drvLcdInit(void)
{
    GPIO_InitTypeDef GPIO_Init_Structure;
    /*##-1- Enable peripherals and GPIO Clocks #################################*/  
    /* Enable the LTDC Clock */
    __HAL_RCC_LTDC_CLK_ENABLE();
    /* Enable GPIO Clock */
    __HAL_RCC_GPIOI_CLK_ENABLE();
    __HAL_RCC_GPIOJ_CLK_ENABLE();
    __HAL_RCC_GPIOK_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();

    /*##-2- Configure peripheral GPIO ##########################################*/
    /******************** LTDC Pins configuration *************************/ 
    /*** LTDC Pins configuration ***/
    /* GPIOE configuration */
    GPIO_Init_Structure.Pin = GPIO_PIN_4;
    GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP;
    GPIO_Init_Structure.Pull = GPIO_NOPULL;
    GPIO_Init_Structure.Speed = GPIO_SPEED_FAST;
    GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;  
    HAL_GPIO_Init(GPIOE, &GPIO_Init_Structure);

    /* GPIOG configuration */
    GPIO_Init_Structure.Pin = GPIO_PIN_12;
    GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP;
    GPIO_Init_Structure.Alternate = GPIO_AF9_LTDC;
    HAL_GPIO_Init(GPIOG, &GPIO_Init_Structure);

    /* GPIOI LTDC alternate configuration */
    GPIO_Init_Structure.Pin = GPIO_PIN_9 | GPIO_PIN_10 | \
        GPIO_PIN_14 | GPIO_PIN_15;
    GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP;
    GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;
    HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure);

    /* GPIOJ configuration */  
    GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \
        GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | \
        GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \
        GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
    GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;
    HAL_GPIO_Init(GPIOJ, &GPIO_Init_Structure);  

    /* GPIOK configuration */  
    GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | \
        GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
    GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;
    HAL_GPIO_Init(GPIOK, &GPIO_Init_Structure);

    /* LCD_DISP GPIO configuration */
    GPIO_Init_Structure.Pin = GPIO_PIN_12;     /* LCD_DISP pin has to be manually controlled */
    GPIO_Init_Structure.Mode = GPIO_MODE_OUTPUT_PP;
    HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure);

    /* LCD_BL_CTRL GPIO configuration */
    GPIO_Init_Structure.Pin = GPIO_PIN_3;  /* LCD_BL_CTRL pin has to be manually controlled */
    GPIO_Init_Structure.Mode = GPIO_MODE_OUTPUT_PP;
    HAL_GPIO_Init(GPIOK, &GPIO_Init_Structure);

    /* Set LTDC Interrupt to the lowest priority */
    HAL_NVIC_SetPriority(LTDC_IRQn, 0xE, 0);

    /* Enable LTDC Interrupt */
    HAL_NVIC_EnableIRQ(LTDC_IRQn); 

    RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct;
    /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */
    /* PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAIN = 192 Mhz */
    /* PLLLCDCLK = PLLSAI_VCO Output/PLLSAIR = 192/5 = 38.4 Mhz */
    /* LTDC clock frequency = PLLLCDCLK / LTDC_PLLSAI_DIVR_4 = 38.4/2 = 19.2Mhz */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
    PeriphClkInitStruct.PLLSAI.PLLSAIN = 192;
    PeriphClkInitStruct.PLLSAI.PLLSAIR = 5;
    PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_2;
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);

    LTDC_LayerCfgTypeDef pLayerCfgBack;

    /* LTDC Initialization -------------------------------------------------------*/
    /* Polarity configuration */
    /* Initialize the horizontal synchronization polarity as active low */
    LtdcHandle.Init.HSPolarity = LTDC_HSPOLARITY_AL;
    /* Initialize the vertical synchronization polarity as active low */ 
    LtdcHandle.Init.VSPolarity = LTDC_VSPOLARITY_AL; 
    /* Initialize the data enable polarity as active low */ 
    LtdcHandle.Init.DEPolarity = LTDC_DEPOLARITY_AL; 
    /* Initialize the pixel clock polarity as input pixel clock */  
    LtdcHandle.Init.PCPolarity = LTDC_PCPOLARITY_IPC;

    /* The RK043FN48H LCD 480x272 is selected */
    /* Timing Configuration */
    LtdcHandle.Init.HorizontalSync = (RK043FN48H_HSYNC - 1);
    LtdcHandle.Init.VerticalSync = (RK043FN48H_VSYNC - 1);
    LtdcHandle.Init.AccumulatedHBP = (RK043FN48H_HSYNC + RK043FN48H_HBP - 1);
    LtdcHandle.Init.AccumulatedVBP = (RK043FN48H_VSYNC + RK043FN48H_VBP - 1);
    LtdcHandle.Init.AccumulatedActiveH = (RK043FN48H_HEIGHT + RK043FN48H_VSYNC + RK043FN48H_VBP - 1);
    LtdcHandle.Init.AccumulatedActiveW = (RK043FN48H_WIDTH + RK043FN48H_HSYNC + RK043FN48H_HBP - 1);
    LtdcHandle.Init.TotalHeigh = (RK043FN48H_HEIGHT + RK043FN48H_VSYNC + RK043FN48H_VBP + RK043FN48H_VFP - 1);
    LtdcHandle.Init.TotalWidth = (RK043FN48H_WIDTH + RK043FN48H_HSYNC + RK043FN48H_HBP + RK043FN48H_HFP - 1); 

    /* Configure R,G,B component values for LCD background color */
    LtdcHandle.Init.Backcolor.Blue = 0;
    LtdcHandle.Init.Backcolor.Green = 0;
    LtdcHandle.Init.Backcolor.Red = 0;

    LtdcHandle.Instance = LTDC;

    /* Layer1 Configuration ------------------------------------------------------*/

    /* Windowing configuration */ 
    pLayerCfgBack.WindowX0 = 0;
    pLayerCfgBack.WindowX1 = LCD_LAYER_COLUMN;
    pLayerCfgBack.WindowY0 = 0;
    pLayerCfgBack.WindowY1 = LCD_LAYER_ROW;
    /* Pixel Format configuration*/ 
    pLayerCfgBack.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
    /* Start Address configuration : frame buffer is located at sdram memory */
    pLayerCfgBack.FBStartAdress = (uint32_t)LcdBufPing;
    /* Alpha constant (255 totally opaque) */
    pLayerCfgBack.Alpha = 255;
    /* Default Color configuration (configure A,R,G,B component values) */
    pLayerCfgBack.Alpha0 = 0;
    pLayerCfgBack.Backcolor.Blue = 0;
    pLayerCfgBack.Backcolor.Green = 0;
    pLayerCfgBack.Backcolor.Red = 0;
    /* Configure blending factors */
    pLayerCfgBack.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;
    pLayerCfgBack.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA;
    /* Configure the number of lines and number of pixels per line */
    pLayerCfgBack.ImageWidth = LCD_LAYER_COLUMN;
    pLayerCfgBack.ImageHeight = LCD_LAYER_ROW;

    /* Configure the LTDC */  
    HAL_LTDC_Init(&LtdcHandle);
    /* Assert display enable LCD_DISP pin */
    HAL_GPIO_WritePin(LCD_DISP_GPIO_PORT, LCD_DISP_PIN, GPIO_PIN_SET);
    /* Assert backlight LCD_BL_CTRL pin */
    HAL_GPIO_WritePin(LCD_BL_CTRL_GPIO_PORT, LCD_BL_CTRL_PIN, GPIO_PIN_SET);
    /* Configure the Background Layer*/
    HAL_LTDC_ConfigLayer(&LtdcHandle, &pLayerCfgBack, 0);
}

void drvLcdDeInit(void)
{
    __HAL_RCC_LTDC_FORCE_RESET();
    __HAL_RCC_LTDC_RELEASE_RESET();
}

void drvLcdBufToggle(void)
{
    CurrentUsedBufId == 0 ? \
        HAL_LTDC_SetAddress(&LtdcHandle, (uint32_t)LcdBufPong, LTDC_LAYER_1) : \
        HAL_LTDC_SetAddress(&LtdcHandle, (uint32_t)LcdBufPing, LTDC_LAYER_1);
    CurrentUsedBufId = (CurrentUsedBufId + 1) & 0x01;
}

uint32_t drvGetIdleBufAddr(void)
{
    return CurrentUsedBufId == 0 ? (uint32_t)LcdBufPong : (uint32_t)LcdBufPing;
}